package ldh.im.fxgui.controller;

import com.fluxvend.svgfx.SvgImageView;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
import javafx.stage.Window;
import ldh.im.ImConfig;
import ldh.im.dto.base.OkResp;
import ldh.im.dto.business.item.InvitationResultList;
import ldh.im.dto.business.webrtc.WebRtcCancelReq;
import ldh.im.dto.business.webrtc.WebRtcInvitationReq;
import ldh.im.dto.business.webrtc.WebRtcInvitationResultReq;
import ldh.im.dto.business.webrtc.WebRtcRoomInfo;
import ldh.im.fxbase.rpc.service.RpcWebRtcService;
import ldh.im.fxbase.util.*;
import ldh.im.fxgui.handle.WebRtcClient;
import ldh.rpc.util.JsonUtil;
import ldh.rpc.TerminalType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public abstract class WebRtcBaseController {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebRtcBaseController.class);

    @FXML protected Node container;
    @FXML protected FlowPane flowPane;
    @FXML private Pane acceptPane;
    @FXML private Pane controlPane;

    @FXML private SvgImageView audioSvgView;

    private boolean audioValue = true;        // 是否开启语音： true 开启， false 关闭

    protected volatile String invitationUserId;       // 视频邀请人
    protected volatile String roomName;
    protected String stageName;
    protected WebRtcClient webRtcClient = null;
    protected boolean isClose = false;
    protected boolean isAudio = false;                // 是否语音通信
    protected boolean isAccept = false;               // 是否为视频接受者
    private boolean isAccepted = false;               // 视频邀请是否被接受
    private MediaPlayer callMediaPlayer = null;       // 播放视频连接呼叫     // 呼叫声音是否已经播放，播放后不再播放

    protected Map<String, Node> userNodeViewMap = new ConcurrentHashMap<>();

    public void setStageName(String stageName) {
        this.stageName = stageName;
    }

    /**
     * 1，发起退房请求
     * 2，关闭连接
     * @param isReceiver
     * @throws Exception
     */
    protected void closeRpc(boolean isReceiver) throws Exception {
        CloseFactory.getInstance().remove(stageName);
        try {
            if (!isReceiver) {
                webRtcClient.exitRoom(roomName);
            }
        } catch (Exception e) {
        }
        if (webRtcClient != null) {
            webRtcClient.close();
        }
    }

    protected void startSignalWebSocket(Set<String> userIdSet, String dialogTitle) {
        ThreadPoolUtil.submit(()->{
            ExceptionUtil.catchException(()->{
                webRtcClient.start();
                boolean canWebRtc = loginAndInvitationOrJoin(isAudio, roomName, userIdSet, dialogTitle);
                if (!canWebRtc) {
                    FxThreadUtil.runLater(()->{
                        DialogUtil.alert("警告", "用户不在线，不能语音聊天");
                    });
                    this.close();
                }
                return null;
            }, dialogTitle);
        });
    }

    /**
     * 登录并邀请人组房进行语音聊天或进房聊天
     * @param isAudio 是否语音聊天
     * @param roomName  房间名称， null: 邀请人组房聊天。 不为空： 进房聊天
     * @param userIdSet 邀请用户
     * @param dialogTitle 提示框标题
     */
    protected boolean loginAndInvitationOrJoin(boolean isAudio, String roomName, Set<String> userIdSet, String dialogTitle) {
        webRtcClient.login();

        if (roomName == null) { //  发起者
            boolean canWebRtc = sendWebRtcInvitation(isAudio, userIdSet);
            return canWebRtc;
        } else {
            this.roomName = roomName;
        }
        return true;
    }

    protected void closeConnection(boolean isReceiver) {
        LOGGER.info("[WebRtcController close], roomName: {}, isReceiver: {}", roomName, isReceiver);
        try {
            FxThreadUtil.runLater(()->{
                Window window = getRoot().getScene().getWindow();
                if (window instanceof Stage) {
                    ((Stage) window).close();
                }
            });

            this.closeRpc(isReceiver);
        } catch (Exception e) {
            LOGGER.error("close error, {}", e);
        }
    }

    protected void exitRoom(String userId, boolean isReceiver) {
        LOGGER.info("{} exit room", userId);
        if (userNodeViewMap.containsKey(userId)) {
            if (userNodeViewMap.size() == 1) {
                closeConnection(isReceiver);
            } else {
                FxThreadUtil.runLater(()-> {
                    Node node = userNodeViewMap.remove(userId);
                    flowPane.getChildren().remove(node);
                });
            }
        }
    }

    @FXML
    private void switchAudioEvent(MouseEvent event) {
        this.audioValue = !this.audioValue;
        if (this.audioValue) {
            this.audioSvgView.setSvg(this.getClass().getResource("/webrtc-img/audio.svg").toExternalForm());
        } else {
            this.audioSvgView.setSvg(this.getClass().getResource("/webrtc-img/audio_close.svg").toExternalForm());
        }
        this.webRtcClient.setAudioStatus(this.audioValue);
    }

    @FXML
    private void acceptEvent(MouseEvent event) {
        this.isAccepted = true;
        initUi();
        ThreadPoolUtil.submit(()->{
            try {
                WebRtcRoomInfo webRtcRoomInfo = webRtcClient.joinRoom(roomName);
                if (webRtcRoomInfo.getUserInfoList() == null || webRtcRoomInfo.getUserInfoList().size() < 1) { // 房间已经关闭
                    closeConnection(true);
                }
                invitationResult(this.invitationUserId, true, null);
            } catch (Exception e) {
                LOGGER.error("accept webrtc invitation error", e);
            }
        });
    }

    @FXML
    private void rejectEvent(MouseEvent event) {
        ThreadPoolUtil.submit(()->{
            try {
                invitationResult(this.invitationUserId, false, "");
                this.close();
            } catch (Exception e) {
                LOGGER.error("send invitation result failure, error: {}", e);
            }
        });
    }

    protected void initUi() {
        if (Objects.isNull(this.roomName) || this.isAccepted) { // 视频发起方
            this.acceptPane.setVisible(false);
            this.controlPane.setVisible(true);
        } else { // 视频接收方
            this.acceptPane.setVisible(true);
            this.controlPane.setVisible(false);
        }
    }

    private void sendWebRtcCancel(Set<String> userIds) {
        if (userIds.size() < 1) return;
        LOGGER.info("[WebRtcController cancel], roomName: {}, userIds: {}", roomName, userIds.stream().collect(Collectors.joining(",")));
        try {
            WebRtcCancelReq cancelReq = new WebRtcCancelReq();
            cancelReq.setUserIds(userIds);
            RpcWebRtcService rpcWebRtcService = RpcUtil.getRpcBootstrap().getRpcConfig().getClientBean(RpcWebRtcService.class);
            OkResp okResp = rpcWebRtcService.sendWebRtcCancel(cancelReq);
            LOGGER.info("[WebRtcController cancel], result: {}", JsonUtil.toJson(okResp));
        } catch (Exception e) {
            LOGGER.error("[WebRtcController cancel] error, {}", e);
        }
    }

    private void invitationResult(String fromUserId, boolean isAccept, String rejectReason) {
        WebRtcInvitationResultReq resultReq = new WebRtcInvitationResultReq();
        resultReq.setAccept(isAccept);
        resultReq.setReason(rejectReason);
        resultReq.setToUserId(fromUserId);
        RpcWebRtcService rpcWebRtcService = RpcUtil.getRpcBootstrap().getRpcConfig().getClientBean(RpcWebRtcService.class);
        OkResp okResp = rpcWebRtcService.sendWebRtcInvitationResult(resultReq);
    }

    private boolean sendWebRtcInvitation(boolean isAudio, Set<String> userIdSet) {
        String userId = RpcUtil.getRpcBootstrap().getRpcConfig().getCacheService().getCache(ImConfig.LOGIN_USER_ID).toString();

        this.roomName = webRtcClient.createRoom(isAudio);
        WebRtcInvitationReq invitationReq = new WebRtcInvitationReq();
        invitationReq.setFromUserId(userId);
        invitationReq.setRoomName(this.roomName);
        invitationReq.setAudio(isAudio);
        userIdSet.remove(userId);
        invitationReq.setUserIds(userIdSet);
        RpcWebRtcService rpcWebRtcService = RpcUtil.getRpcBootstrap().getRpcConfig().getClientBean(RpcWebRtcService.class);
        InvitationResultList avInvitationResultList = rpcWebRtcService.sendWebRtcInvitation(invitationReq);

        long offlineCount = avInvitationResultList.getInvitationResultList().stream().filter(ir->ir.isOffline()).count();
        if (offlineCount == invitationReq.getUserIds().size()) {
            return false;
        }
        return true;
    }

    public void close() {
        if (isClose) return;
        isClose = true;

        // 停止呼叫声音
        this.stopCallSound();
        // 发送取消信息
        this.sendWebRtcCancel();
        // 关闭连接
        closeConnection(false);
    }

    protected void playCallSound() {
        if (this.callMediaPlayer == null) {
            Media media = new Media(this.getClass().getResource("/media/call.mp3").toExternalForm());
            callMediaPlayer = new MediaPlayer(media);
            callMediaPlayer.setCycleCount(20);
        }
        callMediaPlayer.play();
    }

    protected void stopCallSound() {
        if (this.callMediaPlayer != null) {
            callMediaPlayer.stop();
        }
    }

    abstract Node getRoot();

    private void sendWebRtcCancel() {
        try {
            Set<String> connectionUsers = webRtcClient.getConnectedUsers();
            Set<String> notConnectionUsers = new HashSet<>();
            for (String userId : userNodeViewMap.keySet()) {
                if (!connectionUsers.contains(userId)) {
                    notConnectionUsers.add(userId);
                }
            }

            // 向未建立连接的用户发起取消请求
            if (!this.isAccept) {  // 发起者才能取消请求
                sendWebRtcCancel(notConnectionUsers);
            }
        } catch (Exception e) {
            LOGGER.error("sendWebRtcCancel error", e);
        }
    }
}
